1   /*
2    * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.font;
27  
28  import java.awt.Font;
29  import java.awt.FontFormatException;
30  import java.awt.GraphicsEnvironment;
31  import java.awt.geom.Point2D;
32  import java.io.FileNotFoundException;
33  import java.io.IOException;
34  import java.io.RandomAccessFile;
35  import java.io.UnsupportedEncodingException;
36  import java.nio.ByteBuffer;
37  import java.nio.CharBuffer;
38  import java.nio.IntBuffer;
39  import java.nio.ShortBuffer;
40  import java.nio.channels.ClosedChannelException;
41  import java.nio.channels.FileChannel;
42  import java.util.HashMap;
43  import java.util.HashSet;
44  import java.util.Map;
45  import java.util.Locale;
46  import sun.java2d.Disposer;
47  import sun.java2d.DisposerRecord;
48  
49  /**
50   * TrueTypeFont is not called SFntFont because it is not expected
51   * to handle all types that may be housed in a such a font file.
52   * If additional types are supported later, it may make sense to
53   * create an SFnt superclass. Eg to handle sfnt-housed postscript fonts.
54   * OpenType fonts are handled by this class, and possibly should be
55   * represented by a subclass.
56   * An instance stores some information from the font file to faciliate
57   * faster access. File size, the table directory and the names of the font
58   * are the most important of these. It amounts to approx 400 bytes
59   * for a typical font. Systems with mutiple locales sometimes have up to 400
60   * font files, and an app which loads all font files would need around
61   * 160Kbytes. So storing any more info than this would be expensive.
62   */
63  public class TrueTypeFont extends FileFont {
64  
65     /* -- Tags for required TrueType tables */
66      public static final int cmapTag = 0x636D6170; // 'cmap'
67      public static final int glyfTag = 0x676C7966; // 'glyf'
68      public static final int headTag = 0x68656164; // 'head'
69      public static final int hheaTag = 0x68686561; // 'hhea'
70      public static final int hmtxTag = 0x686D7478; // 'hmtx'
71      public static final int locaTag = 0x6C6F6361; // 'loca'
72      public static final int maxpTag = 0x6D617870; // 'maxp'
73      public static final int nameTag = 0x6E616D65; // 'name'
74      public static final int postTag = 0x706F7374; // 'post'
75      public static final int os_2Tag = 0x4F532F32; // 'OS/2'
76  
77      /* -- Tags for opentype related tables */
78      public static final int GDEFTag = 0x47444546; // 'GDEF'
79      public static final int GPOSTag = 0x47504F53; // 'GPOS'
80      public static final int GSUBTag = 0x47535542; // 'GSUB'
81      public static final int mortTag = 0x6D6F7274; // 'mort'
82  
83      /* -- Tags for non-standard tables */
84      public static final int fdscTag = 0x66647363; // 'fdsc' - gxFont descriptor
85      public static final int fvarTag = 0x66766172; // 'fvar' - gxFont variations
86      public static final int featTag = 0x66656174; // 'feat' - layout features
87      public static final int EBLCTag = 0x45424C43; // 'EBLC' - embedded bitmaps
88      public static final int gaspTag = 0x67617370; // 'gasp' - hint/smooth sizes
89  
90      /* --  Other tags */
91      public static final int ttcfTag = 0x74746366; // 'ttcf' - TTC file
92      public static final int v1ttTag = 0x00010000; // 'v1tt' - Version 1 TT font
93      public static final int trueTag = 0x74727565; // 'true' - Version 2 TT font
94      public static final int ottoTag = 0x4f54544f; // 'otto' - OpenType font
95  
96      /* -- ID's used in the 'name' table */
97      public static final int MS_PLATFORM_ID = 3;
98      /* MS locale id for US English is the "default" */
99      public static final short ENGLISH_LOCALE_ID = 0x0409; // 1033 decimal
100     public static final int FAMILY_NAME_ID = 1;
101     // public static final int STYLE_WEIGHT_ID = 2; // currently unused.
102     public static final int FULL_NAME_ID = 4;
103     public static final int POSTSCRIPT_NAME_ID = 6;
104 
105     private static final short US_LCID = 0x0409;  // US English - default
106 
107     private static Map<String, Short> lcidMap;
108 
109     class DirectoryEntry {
110         int tag;
111         int offset;
112         int length;
113     }
114 
115     /* There is a pool which limits the number of fd's that are in
116      * use. Normally fd's are closed as they are replaced in the pool.
117      * But if an instance of this class becomes unreferenced, then there
118      * needs to be a way to close the fd. A finalize() method could do this,
119      * but using the Disposer class will ensure its called in a more timely
120      * manner. This is not something which should be relied upon to free
121      * fd's - its a safeguard.
122      */
123     private static class TTDisposerRecord implements DisposerRecord {
124 
125         FileChannel channel = null;
126 
127         public synchronized void dispose() {
128             try {
129                 if (channel != null) {
130                     channel.close();
131                 }
132             } catch (IOException e) {
133             } finally {
134                 channel = null;
135             }
136         }
137     }
138 
139     TTDisposerRecord disposerRecord = new TTDisposerRecord();
140 
141     /* > 0 only if this font is a part of a collection */
142     int fontIndex = 0;
143 
144     /* Number of fonts in this collection. ==1 if not a collection */
145     int directoryCount = 1;
146 
147     /* offset in file of table directory for this font */
148     int directoryOffset; // 12 if its not a collection.
149 
150     /* number of table entries in the directory/offsets table */
151     int numTables;
152 
153     /* The contents of the the directory/offsets table */
154     DirectoryEntry []tableDirectory;
155 
156 //     protected byte []gposTable = null;
157 //     protected byte []gdefTable = null;
158 //     protected byte []gsubTable = null;
159 //     protected byte []mortTable = null;
160 //     protected boolean hintsTabledChecked = false;
161 //     protected boolean containsHintsTable = false;
162 
163     /* These fields are set from os/2 table info. */
164     private boolean supportsJA;
165     private boolean supportsCJK;
166 
167     /* These are for faster access to the name of the font as
168      * typically exposed via API to applications.
169      */
170     private Locale nameLocale;
171     private String localeFamilyName;
172     private String localeFullName;
173 
174     /**
175      * - does basic verification of the file
176      * - reads the header table for this font (within a collection)
177      * - reads the names (full, family).
178      * - determines the style of the font.
179      * - initializes the CMAP
180      * @throws FontFormatException - if the font can't be opened
181      * or fails verification,  or there's no usable cmap
182      */
183     public TrueTypeFont(String platname, Object nativeNames, int fIndex,
184                  boolean javaRasterizer)
185         throws FontFormatException {
186         super(platname, nativeNames);
187         useJavaRasterizer = javaRasterizer;
188         fontRank = Font2D.TTF_RANK;
189         try {
190             verify();
191             init(fIndex);
192         } catch (Throwable t) {
193             close();
194             if (t instanceof FontFormatException) {
195                 throw (FontFormatException)t;
196             } else {
197                 throw new FontFormatException("Unexpected runtime exception.");
198             }
199         }
200         Disposer.addObjectRecord(this, disposerRecord);
201     }
202 
203     /* Enable natives just for fonts picked up from the platform that
204      * may have external bitmaps on Solaris. Could do this just for
205      * the fonts that are specified in font configuration files which
206      * would lighten the burden (think about that).
207      * The EBLCTag is used to skip natives for fonts that contain embedded
208      * bitmaps as there's no need to use X11 for those fonts.
209      * Skip all the latin fonts as they don't need this treatment.
210      * Further refine this to fonts that are natively accessible (ie
211      * as PCF bitmap fonts on the X11 font path).
212      * This method is called when creating the first strike for this font.
213      */
214     @Override
215     protected boolean checkUseNatives() {
216         if (checkedNatives) {
217             return useNatives;
218         }
219         if (!FontUtilities.isSolaris || useJavaRasterizer ||
220             FontUtilities.useT2K || nativeNames == null ||
221             getDirectoryEntry(EBLCTag) != null ||
222             GraphicsEnvironment.isHeadless()) {
223             checkedNatives = true;
224             return false; /* useNatives is false */
225         } else if (nativeNames instanceof String) {
226             String name = (String)nativeNames;
227             /* Don't do do this for Latin fonts */
228             if (name.indexOf("8859") > 0) {
229                 checkedNatives = true;
230                 return false;
231             } else if (NativeFont.hasExternalBitmaps(name)) {
232                 nativeFonts = new NativeFont[1];
233                 try {
234                     nativeFonts[0] = new NativeFont(name, true);
235                     /* If reach here we have an non-latin font that has
236                      * external bitmaps and we successfully created it.
237                      */
238                     useNatives = true;
239                 } catch (FontFormatException e) {
240                     nativeFonts = null;
241                 }
242             }
243         } else if (nativeNames instanceof String[]) {
244             String[] natNames = (String[])nativeNames;
245             int numNames = natNames.length;
246             boolean externalBitmaps = false;
247             for (int nn = 0; nn < numNames; nn++) {
248                 if (natNames[nn].indexOf("8859") > 0) {
249                     checkedNatives = true;
250                     return false;
251                 } else if (NativeFont.hasExternalBitmaps(natNames[nn])) {
252                     externalBitmaps = true;
253                 }
254             }
255             if (!externalBitmaps) {
256                 checkedNatives = true;
257                 return false;
258             }
259             useNatives = true;
260             nativeFonts = new NativeFont[numNames];
261             for (int nn = 0; nn < numNames; nn++) {
262                 try {
263                     nativeFonts[nn] = new NativeFont(natNames[nn], true);
264                 } catch (FontFormatException e) {
265                     useNatives = false;
266                     nativeFonts = null;
267                 }
268             }
269         }
270         if (useNatives) {
271             glyphToCharMap = new char[getMapper().getNumGlyphs()];
272         }
273         checkedNatives = true;
274         return useNatives;
275     }
276 
277 
278     /* This is intended to be called, and the returned value used,
279      * from within a block synchronized on this font object.
280      * ie the channel returned may be nulled out at any time by "close()"
281      * unless the caller holds a lock.
282      * Deadlock warning: FontManager.addToPool(..) acquires a global lock,
283      * which means nested locks may be in effect.
284      */
285     private synchronized FileChannel open() throws FontFormatException {
286         if (disposerRecord.channel == null) {
287             if (FontUtilities.isLogging()) {
288                 FontUtilities.getLogger().info("open TTF: " + platName);
289             }
290             try {
291                 RandomAccessFile raf = (RandomAccessFile)
292                 java.security.AccessController.doPrivileged(
293                     new java.security.PrivilegedAction() {
294                         public Object run() {
295                             try {
296                                 return new RandomAccessFile(platName, "r");
297                             } catch (FileNotFoundException ffne) {
298                             }
299                             return null;
300                     }
301                 });
302                 disposerRecord.channel = raf.getChannel();
303                 fileSize = (int)disposerRecord.channel.size();
304                 FontManager fm = FontManagerFactory.getInstance();
305                 if (fm instanceof SunFontManager) {
306                     ((SunFontManager) fm).addToPool(this);
307                 }
308             } catch (NullPointerException e) {
309                 close();
310                 throw new FontFormatException(e.toString());
311             } catch (ClosedChannelException e) {
312                 /* NIO I/O is interruptible, recurse to retry operation.
313                  * The call to channel.size() above can throw this exception.
314                  * Clear interrupts before recursing in case NIO didn't.
315                  * Note that close() sets disposerRecord.channel to null.
316                  */
317                 Thread.interrupted();
318                 close();
319                 open();
320             } catch (IOException e) {
321                 close();
322                 throw new FontFormatException(e.toString());
323             }
324         }
325         return disposerRecord.channel;
326     }
327 
328     protected synchronized void close() {
329         disposerRecord.dispose();
330     }
331 
332 
333     int readBlock(ByteBuffer buffer, int offset, int length) {
334         int bread = 0;
335         try {
336             synchronized (this) {
337                 if (disposerRecord.channel == null) {
338                     open();
339                 }
340                 if (offset + length > fileSize) {
341                     if (offset >= fileSize) {
342                         /* Since the caller ensures that offset is < fileSize
343                          * this condition suggests that fileSize is now
344                          * different than the value we originally provided
345                          * to native when the scaler was created.
346                          * Also fileSize is updated every time we
347                          * open() the file here, but in native the value
348                          * isn't updated. If the file has changed whilst we
349                          * are executing we want to bail, not spin.
350                          */
351                         if (FontUtilities.isLogging()) {
352                             String msg = "Read offset is " + offset +
353                                 " file size is " + fileSize+
354                                 " file is " + platName;
355                             FontUtilities.getLogger().severe(msg);
356                         }
357                         return -1;
358                     } else {
359                         length = fileSize - offset;
360                     }
361                 }
362                 buffer.clear();
363                 disposerRecord.channel.position(offset);
364                 while (bread < length) {
365                     int cnt = disposerRecord.channel.read(buffer);
366                     if (cnt == -1) {
367                         String msg = "Unexpected EOF " + this;
368                         int currSize = (int)disposerRecord.channel.size();
369                         if (currSize != fileSize) {
370                             msg += " File size was " + fileSize +
371                                 " and now is " + currSize;
372                         }
373                         if (FontUtilities.isLogging()) {
374                             FontUtilities.getLogger().severe(msg);
375                         }
376                         // We could still flip() the buffer here because
377                         // it's possible that we did read some data in
378                         // an earlier loop, and we probably should
379                         // return that to the caller. Although if
380                         // the caller expected 8K of data and we return
381                         // only a few bytes then maybe it's better instead to
382                         // set bread = -1 to indicate failure.
383                         // The following is therefore using arbitrary values
384                         // but is meant to allow cases where enough
385                         // data was read to probably continue.
386                         if (bread > length/2 || bread > 16384) {
387                             buffer.flip();
388                             if (FontUtilities.isLogging()) {
389                                 msg = "Returning " + bread +
390                                     " bytes instead of " + length;
391                                 FontUtilities.getLogger().severe(msg);
392                             }
393                         } else {
394                             bread = -1;
395                         }
396                         throw new IOException(msg);
397                     }
398                     bread += cnt;
399                 }
400                 buffer.flip();
401                 if (bread > length) { // possible if buffer.size() > length
402                     bread = length;
403                 }
404             }
405         } catch (FontFormatException e) {
406             if (FontUtilities.isLogging()) {
407                 FontUtilities.getLogger().severe(
408                                        "While reading " + platName, e);
409             }
410             bread = -1; // signal EOF
411             deregisterFontAndClearStrikeCache();
412         } catch (ClosedChannelException e) {
413             /* NIO I/O is interruptible, recurse to retry operation.
414              * Clear interrupts before recursing in case NIO didn't.
415              */
416             Thread.interrupted();
417             close();
418             return readBlock(buffer, offset, length);
419         } catch (IOException e) {
420             /* If we did not read any bytes at all and the exception is
421              * not a recoverable one (ie is not ClosedChannelException) then
422              * we should indicate that there is no point in re-trying.
423              * Other than an attempt to read past the end of the file it
424              * seems unlikely this would occur as problems opening the
425              * file are handled as a FontFormatException.
426              */
427             if (FontUtilities.isLogging()) {
428                 FontUtilities.getLogger().severe(
429                                        "While reading " + platName, e);
430             }
431             if (bread == 0) {
432                 bread = -1; // signal EOF
433                 deregisterFontAndClearStrikeCache();
434             }
435         }
436         return bread;
437     }
438 
439     ByteBuffer readBlock(int offset, int length) {
440 
441         ByteBuffer buffer = ByteBuffer.allocate(length);
442         try {
443             synchronized (this) {
444                 if (disposerRecord.channel == null) {
445                     open();
446                 }
447                 if (offset + length > fileSize) {
448                     if (offset > fileSize) {
449                         return null; // assert?
450                     } else {
451                         buffer = ByteBuffer.allocate(fileSize-offset);
452                     }
453                 }
454                 disposerRecord.channel.position(offset);
455                 disposerRecord.channel.read(buffer);
456                 buffer.flip();
457             }
458         } catch (FontFormatException e) {
459             return null;
460         } catch (ClosedChannelException e) {
461             /* NIO I/O is interruptible, recurse to retry operation.
462              * Clear interrupts before recursing in case NIO didn't.
463              */
464             Thread.interrupted();
465             close();
466             readBlock(buffer, offset, length);
467         } catch (IOException e) {
468             return null;
469         }
470         return buffer;
471     }
472 
473     /* This is used by native code which can't allocate a direct byte
474      * buffer because of bug 4845371. It, and references to it in native
475      * code in scalerMethods.c can be removed once that bug is fixed.
476      * 4845371 is now fixed but we'll keep this around as it doesn't cost
477      * us anything if its never used/called.
478      */
479     byte[] readBytes(int offset, int length) {
480         ByteBuffer buffer = readBlock(offset, length);
481         if (buffer.hasArray()) {
482             return buffer.array();
483         } else {
484             byte[] bufferBytes = new byte[buffer.limit()];
485             buffer.get(bufferBytes);
486             return bufferBytes;
487         }
488     }
489 
490     private void verify() throws FontFormatException {
491         open();
492     }
493 
494     /* sizes, in bytes, of TT/TTC header records */
495     private static final int TTCHEADERSIZE = 12;
496     private static final int DIRECTORYHEADERSIZE = 12;
497     private static final int DIRECTORYENTRYSIZE = 16;
498 
499     protected void init(int fIndex) throws FontFormatException  {
500         int headerOffset = 0;
501         ByteBuffer buffer = readBlock(0, TTCHEADERSIZE);
502         try {
503             switch (buffer.getInt()) {
504 
505             case ttcfTag:
506                 buffer.getInt(); // skip TTC version ID
507                 directoryCount = buffer.getInt();
508                 if (fIndex >= directoryCount) {
509                     throw new FontFormatException("Bad collection index");
510                 }
511                 fontIndex = fIndex;
512                 buffer = readBlock(TTCHEADERSIZE+4*fIndex, 4);
513                 headerOffset = buffer.getInt();
514                 break;
515 
516             case v1ttTag:
517             case trueTag:
518             case ottoTag:
519                 break;
520 
521             default:
522                 throw new FontFormatException("Unsupported sfnt " +
523                                               getPublicFileName());
524             }
525 
526             /* Now have the offset of this TT font (possibly within a TTC)
527              * After the TT version/scaler type field, is the short
528              * representing the number of tables in the table directory.
529              * The table directory begins at 12 bytes after the header.
530              * Each table entry is 16 bytes long (4 32-bit ints)
531              */
532             buffer = readBlock(headerOffset+4, 2);
533             numTables = buffer.getShort();
534             directoryOffset = headerOffset+DIRECTORYHEADERSIZE;
535             ByteBuffer bbuffer = readBlock(directoryOffset,
536                                            numTables*DIRECTORYENTRYSIZE);
537             IntBuffer ibuffer = bbuffer.asIntBuffer();
538             DirectoryEntry table;
539             tableDirectory = new DirectoryEntry[numTables];
540             for (int i=0; i<numTables;i++) {
541                 tableDirectory[i] = table = new DirectoryEntry();
542                 table.tag   =  ibuffer.get();
543                 /* checksum */ ibuffer.get();
544                 table.offset = ibuffer.get();
545                 table.length = ibuffer.get();
546                 if (table.offset + table.length > fileSize) {
547                     throw new FontFormatException("bad table, tag="+table.tag);
548                 }
549             }
550             initNames();
551         } catch (Exception e) {
552             if (FontUtilities.isLogging()) {
553                 FontUtilities.getLogger().severe(e.toString());
554             }
555             if (e instanceof FontFormatException) {
556                 throw (FontFormatException)e;
557             } else {
558                 throw new FontFormatException(e.toString());
559             }
560         }
561         if (familyName == null || fullName == null) {
562             throw new FontFormatException("Font name not found");
563         }
564         /* The os2_Table is needed to gather some info, but we don't
565          * want to keep it around (as a field) so obtain it once and
566          * pass it to the code that needs it.
567          */
568         ByteBuffer os2_Table = getTableBuffer(os_2Tag);
569         setStyle(os2_Table);
570         setCJKSupport(os2_Table);
571     }
572 
573     /* The array index corresponds to a bit offset in the TrueType
574      * font's OS/2 compatibility table's code page ranges fields.
575      * These are two 32 bit unsigned int fields at offsets 78 and 82.
576      * We are only interested in determining if the font supports
577      * the windows encodings we expect as the default encoding in
578      * supported locales, so we only map the first of these fields.
579      */
580     static final String encoding_mapping[] = {
581         "cp1252",    /*  0:Latin 1  */
582         "cp1250",    /*  1:Latin 2  */
583         "cp1251",    /*  2:Cyrillic */
584         "cp1253",    /*  3:Greek    */
585         "cp1254",    /*  4:Turkish/Latin 5  */
586         "cp1255",    /*  5:Hebrew   */
587         "cp1256",    /*  6:Arabic   */
588         "cp1257",    /*  7:Windows Baltic   */
589         "",          /*  8:reserved for alternate ANSI */
590         "",          /*  9:reserved for alternate ANSI */
591         "",          /* 10:reserved for alternate ANSI */
592         "",          /* 11:reserved for alternate ANSI */
593         "",          /* 12:reserved for alternate ANSI */
594         "",          /* 13:reserved for alternate ANSI */
595         "",          /* 14:reserved for alternate ANSI */
596         "",          /* 15:reserved for alternate ANSI */
597         "ms874",     /* 16:Thai     */
598         "ms932",     /* 17:JIS/Japanese */
599         "gbk",       /* 18:PRC GBK Cp950  */
600         "ms949",     /* 19:Korean Extended Wansung */
601         "ms950",     /* 20:Chinese (Taiwan, Hongkong, Macau) */
602         "ms1361",    /* 21:Korean Johab */
603         "",          /* 22 */
604         "",          /* 23 */
605         "",          /* 24 */
606         "",          /* 25 */
607         "",          /* 26 */
608         "",          /* 27 */
609         "",          /* 28 */
610         "",          /* 29 */
611         "",          /* 30 */
612         "",          /* 31 */
613     };
614 
615     /* This maps two letter language codes to a Windows code page.
616      * Note that eg Cp1252 (the first subarray) is not exactly the same as
617      * Latin-1 since Windows code pages are do not necessarily correspond.
618      * There are two codepages for zh and ko so if a font supports
619      * only one of these ranges then we need to distinguish based on
620      * country. So far this only seems to matter for zh.
621      * REMIND: Unicode locales such as Hindi do not have a code page so
622      * this whole mechansim needs to be revised to map languages to
623      * the Unicode ranges either when this fails, or as an additional
624      * validating test. Basing it on Unicode ranges should get us away
625      * from needing to map to this small and incomplete set of Windows
626      * code pages which looks odd on non-Windows platforms.
627      */
628     private static final String languages[][] = {
629 
630         /* cp1252/Latin 1 */
631         { "en", "ca", "da", "de", "es", "fi", "fr", "is", "it",
632           "nl", "no", "pt", "sq", "sv", },
633 
634          /* cp1250/Latin2 */
635         { "cs", "cz", "et", "hr", "hu", "nr", "pl", "ro", "sk",
636           "sl", "sq", "sr", },
637 
638         /* cp1251/Cyrillic */
639         { "bg", "mk", "ru", "sh", "uk" },
640 
641         /* cp1253/Greek*/
642         { "el" },
643 
644          /* cp1254/Turkish,Latin 5 */
645         { "tr" },
646 
647          /* cp1255/Hebrew */
648         { "he" },
649 
650         /* cp1256/Arabic */
651         { "ar" },
652 
653          /* cp1257/Windows Baltic */
654         { "et", "lt", "lv" },
655 
656         /* ms874/Thai */
657         { "th" },
658 
659          /* ms932/Japanese */
660         { "ja" },
661 
662         /* gbk/Chinese (PRC GBK Cp950) */
663         { "zh", "zh_CN", },
664 
665         /* ms949/Korean Extended Wansung */
666         { "ko" },
667 
668         /* ms950/Chinese (Taiwan, Hongkong, Macau) */
669         { "zh_HK", "zh_TW", },
670 
671         /* ms1361/Korean Johab */
672         { "ko" },
673     };
674 
675     private static final String codePages[] = {
676         "cp1252",
677         "cp1250",
678         "cp1251",
679         "cp1253",
680         "cp1254",
681         "cp1255",
682         "cp1256",
683         "cp1257",
684         "ms874",
685         "ms932",
686         "gbk",
687         "ms949",
688         "ms950",
689         "ms1361",
690     };
691 
692     private static String defaultCodePage = null;
693     static String getCodePage() {
694 
695         if (defaultCodePage != null) {
696             return defaultCodePage;
697         }
698 
699         if (FontUtilities.isWindows) {
700             defaultCodePage =
701                 (String)java.security.AccessController.doPrivileged(
702                    new sun.security.action.GetPropertyAction("file.encoding"));
703         } else {
704             if (languages.length != codePages.length) {
705                 throw new InternalError("wrong code pages array length");
706             }
707             Locale locale = sun.awt.SunToolkit.getStartupLocale();
708 
709             String language = locale.getLanguage();
710             if (language != null) {
711                 if (language.equals("zh")) {
712                     String country = locale.getCountry();
713                     if (country != null) {
714                         language = language + "_" + country;
715                     }
716                 }
717                 for (int i=0; i<languages.length;i++) {
718                     for (int l=0;l<languages[i].length; l++) {
719                         if (language.equals(languages[i][l])) {
720                             defaultCodePage = codePages[i];
721                             return defaultCodePage;
722                         }
723                     }
724                 }
725             }
726         }
727         if (defaultCodePage == null) {
728             defaultCodePage = "";
729         }
730         return defaultCodePage;
731     }
732 
733     /* Theoretically, reserved bits must not be set, include symbol bits */
734     public static final int reserved_bits1 = 0x80000000;
735     public static final int reserved_bits2 = 0x0000ffff;
736     @Override
737     boolean supportsEncoding(String encoding) {
738         if (encoding == null) {
739             encoding = getCodePage();
740         }
741         if ("".equals(encoding)) {
742             return false;
743         }
744 
745         encoding = encoding.toLowerCase();
746 
747         /* java_props_md.c has a couple of special cases
748          * if language packs are installed. In these encodings the
749          * fontconfig files pick up different fonts :
750          * SimSun-18030 and MingLiU_HKSCS. Since these fonts will
751          * indicate they support the base encoding, we need to rewrite
752          * these encodings here before checking the map/array.
753          */
754         if (encoding.equals("gb18030")) {
755             encoding = "gbk";
756         } else if (encoding.equals("ms950_hkscs")) {
757             encoding = "ms950";
758         }
759 
760         ByteBuffer buffer = getTableBuffer(os_2Tag);
761         /* required info is at offsets 78 and 82 */
762         if (buffer == null || buffer.capacity() < 86) {
763             return false;
764         }
765 
766         int range1 = buffer.getInt(78); /* ulCodePageRange1 */
767         int range2 = buffer.getInt(82); /* ulCodePageRange2 */
768 
769         /* This test is too stringent for Arial on Solaris (and perhaps
770          * other fonts). Arial has at least one reserved bit set for an
771          * unknown reason.
772          */
773 //         if (((range1 & reserved_bits1) | (range2 & reserved_bits2)) != 0) {
774 //             return false;
775 //         }
776 
777         for (int em=0; em<encoding_mapping.length; em++) {
778             if (encoding_mapping[em].equals(encoding)) {
779                 if (((1 << em) & range1) != 0) {
780                     return true;
781                 }
782             }
783         }
784         return false;
785     }
786 
787 
788     /* Use info in the os_2Table to test CJK support */
789     private void setCJKSupport(ByteBuffer os2Table) {
790         /* required info is in ulong at offset 46 */
791         if (os2Table == null || os2Table.capacity() < 50) {
792             return;
793         }
794         int range2 = os2Table.getInt(46); /* ulUnicodeRange2 */
795 
796         /* Any of these bits set in the 32-63 range indicate a font with
797          * support for a CJK range. We aren't looking at some other bits
798          * in the 64-69 range such as half width forms as its unlikely a font
799          * would include those and none of these.
800          */
801         supportsCJK = ((range2 & 0x29bf0000) != 0);
802 
803         /* This should be generalised, but for now just need to know if
804          * Hiragana or Katakana ranges are supported by the font.
805          * In the 4 longs representing unicode ranges supported
806          * bits 49 & 50 indicate hiragana and katakana
807          * This is bits 17 & 18 in the 2nd ulong. If either is supported
808          * we presume this is a JA font.
809          */
810         supportsJA = ((range2 & 0x60000) != 0);
811     }
812 
813     boolean supportsJA() {
814         return supportsJA;
815     }
816 
817      ByteBuffer getTableBuffer(int tag) {
818         DirectoryEntry entry = null;
819 
820         for (int i=0;i<numTables;i++) {
821             if (tableDirectory[i].tag == tag) {
822                 entry = tableDirectory[i];
823                 break;
824             }
825         }
826         if (entry == null || entry.length == 0 ||
827             entry.offset+entry.length > fileSize) {
828             return null;
829         }
830 
831         int bread = 0;
832         ByteBuffer buffer = ByteBuffer.allocate(entry.length);
833         synchronized (this) {
834             try {
835                 if (disposerRecord.channel == null) {
836                     open();
837                 }
838                 disposerRecord.channel.position(entry.offset);
839                 bread = disposerRecord.channel.read(buffer);
840                 buffer.flip();
841             } catch (ClosedChannelException e) {
842                 /* NIO I/O is interruptible, recurse to retry operation.
843                  * Clear interrupts before recursing in case NIO didn't.
844                  */
845                 Thread.interrupted();
846                 close();
847                 return getTableBuffer(tag);
848             } catch (IOException e) {
849                 return null;
850             } catch (FontFormatException e) {
851                 return null;
852             }
853 
854             if (bread < entry.length) {
855                 return null;
856             } else {
857                 return buffer;
858             }
859         }
860     }
861 
862     /* NB: is it better to move declaration to Font2D? */
863     long getLayoutTableCache() {
864         try {
865           return getScaler().getLayoutTableCache();
866         } catch(FontScalerException fe) {
867             return 0L;
868         }
869     }
870 
871     @Override
872     byte[] getTableBytes(int tag) {
873         ByteBuffer buffer = getTableBuffer(tag);
874         if (buffer == null) {
875             return null;
876         } else if (buffer.hasArray()) {
877             try {
878                 return buffer.array();
879             } catch (Exception re) {
880             }
881         }
882         byte []data = new byte[getTableSize(tag)];
883         buffer.get(data);
884         return data;
885     }
886 
887     int getTableSize(int tag) {
888         for (int i=0;i<numTables;i++) {
889             if (tableDirectory[i].tag == tag) {
890                 return tableDirectory[i].length;
891             }
892         }
893         return 0;
894     }
895 
896     int getTableOffset(int tag) {
897         for (int i=0;i<numTables;i++) {
898             if (tableDirectory[i].tag == tag) {
899                 return tableDirectory[i].offset;
900             }
901         }
902         return 0;
903     }
904 
905     DirectoryEntry getDirectoryEntry(int tag) {
906         for (int i=0;i<numTables;i++) {
907             if (tableDirectory[i].tag == tag) {
908                 return tableDirectory[i];
909             }
910         }
911         return null;
912     }
913 
914     /* Used to determine if this size has embedded bitmaps, which
915      * for CJK fonts should be used in preference to LCD glyphs.
916      */
917     boolean useEmbeddedBitmapsForSize(int ptSize) {
918         if (!supportsCJK) {
919             return false;
920         }
921         if (getDirectoryEntry(EBLCTag) == null) {
922             return false;
923         }
924         ByteBuffer eblcTable = getTableBuffer(EBLCTag);
925         int numSizes = eblcTable.getInt(4);
926         /* The bitmapSizeTable's start at offset of 8.
927          * Each bitmapSizeTable entry is 48 bytes.
928          * The offset of ppemY in the entry is 45.
929          */
930         for (int i=0;i<numSizes;i++) {
931             int ppemY = eblcTable.get(8+(i*48)+45) &0xff;
932             if (ppemY == ptSize) {
933                 return true;
934             }
935         }
936         return false;
937     }
938 
939     public String getFullName() {
940         return fullName;
941     }
942 
943     /* This probably won't get called but is there to support the
944      * contract() of setStyle() defined in the superclass.
945      */
946     @Override
947     protected void setStyle() {
948         setStyle(getTableBuffer(os_2Tag));
949     }
950 
951     /* TrueTypeFont can use the fsSelection fields of OS/2 table
952      * to determine the style. In the unlikely case that doesn't exist,
953      * can use macStyle in the 'head' table but simpler to
954      * fall back to super class algorithm of looking for well known string.
955      * A very few fonts don't specify this information, but I only
956      * came across one: Lucida Sans Thai Typewriter Oblique in
957      * /usr/openwin/lib/locale/th_TH/X11/fonts/TrueType/lucidai.ttf
958      * that explicitly specified the wrong value. It says its regular.
959      * I didn't find any fonts that were inconsistent (ie regular plus some
960      * other value).
961      */
962     private static final int fsSelectionItalicBit  = 0x00001;
963     private static final int fsSelectionBoldBit    = 0x00020;
964     private static final int fsSelectionRegularBit = 0x00040;
965     private void setStyle(ByteBuffer os_2Table) {
966         /* fsSelection is unsigned short at buffer offset 62 */
967         if (os_2Table == null || os_2Table.capacity() < 64) {
968             super.setStyle();
969             return;
970         }
971         int fsSelection = os_2Table.getChar(62) & 0xffff;
972         int italic  = fsSelection & fsSelectionItalicBit;
973         int bold    = fsSelection & fsSelectionBoldBit;
974         int regular = fsSelection & fsSelectionRegularBit;
975 //      System.out.println("platname="+platName+" font="+fullName+
976 //                         " family="+familyName+
977 //                         " R="+regular+" I="+italic+" B="+bold);
978         if (regular!=0 && ((italic|bold)!=0)) {
979             /* This is inconsistent. Try using the font name algorithm */
980             super.setStyle();
981             return;
982         } else if ((regular|italic|bold) == 0) {
983             /* No style specified. Try using the font name algorithm */
984             super.setStyle();
985             return;
986         }
987         switch (bold|italic) {
988         case fsSelectionItalicBit:
989             style = Font.ITALIC;
990             break;
991         case fsSelectionBoldBit:
992             if (FontUtilities.isSolaris && platName.endsWith("HG-GothicB.ttf")) {
993                 /* Workaround for Solaris's use of a JA font that's marked as
994                  * being designed bold, but is used as a PLAIN font.
995                  */
996                 style = Font.PLAIN;
997             } else {
998                 style = Font.BOLD;
999             }
1000             break;
1001         case fsSelectionBoldBit|fsSelectionItalicBit:
1002             style = Font.BOLD|Font.ITALIC;
1003         }
1004     }
1005 
1006     private float stSize, stPos, ulSize, ulPos;
1007 
1008     private void setStrikethroughMetrics(ByteBuffer os_2Table, int upem) {
1009         if (os_2Table == null || os_2Table.capacity() < 30 || upem < 0) {
1010             stSize = .05f;
1011             stPos = -.4f;
1012             return;
1013         }
1014         ShortBuffer sb = os_2Table.asShortBuffer();
1015         stSize = sb.get(13) / (float)upem;
1016         stPos = -sb.get(14) / (float)upem;
1017     }
1018 
1019     private void setUnderlineMetrics(ByteBuffer postTable, int upem) {
1020         if (postTable == null || postTable.capacity() < 12 || upem < 0) {
1021             ulSize = .05f;
1022             ulPos = .1f;
1023             return;
1024         }
1025         ShortBuffer sb = postTable.asShortBuffer();
1026         ulSize = sb.get(5) / (float)upem;
1027         ulPos = -sb.get(4) / (float)upem;
1028     }
1029 
1030     @Override
1031     public void getStyleMetrics(float pointSize, float[] metrics, int offset) {
1032 
1033         if (ulSize == 0f && ulPos == 0f) {
1034 
1035             ByteBuffer head_Table = getTableBuffer(headTag);
1036             int upem = -1;
1037             if (head_Table != null && head_Table.capacity() >= 18) {
1038                 ShortBuffer sb = head_Table.asShortBuffer();
1039                 upem = sb.get(9) & 0xffff;
1040             }
1041 
1042             ByteBuffer os2_Table = getTableBuffer(os_2Tag);
1043             setStrikethroughMetrics(os2_Table, upem);
1044 
1045             ByteBuffer post_Table = getTableBuffer(postTag);
1046             setUnderlineMetrics(post_Table, upem);
1047         }
1048 
1049         metrics[offset] = stPos * pointSize;
1050         metrics[offset+1] = stSize * pointSize;
1051 
1052         metrics[offset+2] = ulPos * pointSize;
1053         metrics[offset+3] = ulSize * pointSize;
1054     }
1055 
1056     private String makeString(byte[] bytes, int len, short encoding) {
1057 
1058         /* Check for fonts using encodings 2->6 is just for
1059          * some old DBCS fonts, apparently mostly on Solaris.
1060          * Some of these fonts encode ascii names as double-byte characters.
1061          * ie with a leading zero byte for what properly should be a
1062          * single byte-char.
1063          */
1064         if (encoding >=2 && encoding <= 6) {
1065              byte[] oldbytes = bytes;
1066              int oldlen = len;
1067              bytes = new byte[oldlen];
1068              len = 0;
1069              for (int i=0; i<oldlen; i++) {
1070                  if (oldbytes[i] != 0) {
1071                      bytes[len++] = oldbytes[i];
1072                  }
1073              }
1074          }
1075 
1076         String charset;
1077         switch (encoding) {
1078             case 1:  charset = "UTF-16";  break; // most common case first.
1079             case 0:  charset = "UTF-16";  break; // symbol uses this
1080             case 2:  charset = "SJIS";    break;
1081             case 3:  charset = "GBK";     break;
1082             case 4:  charset = "MS950";   break;
1083             case 5:  charset = "EUC_KR";  break;
1084             case 6:  charset = "Johab";   break;
1085             default: charset = "UTF-16";  break;
1086         }
1087 
1088         try {
1089             return new String(bytes, 0, len, charset);
1090         } catch (UnsupportedEncodingException e) {
1091             if (FontUtilities.isLogging()) {
1092                 FontUtilities.getLogger().warning(e + " EncodingID=" + encoding);
1093             }
1094             return new String(bytes, 0, len);
1095         } catch (Throwable t) {
1096             return null;
1097         }
1098     }
1099 
1100     protected void initNames() {
1101 
1102         byte[] name = new byte[256];
1103         ByteBuffer buffer = getTableBuffer(nameTag);
1104 
1105         if (buffer != null) {
1106             ShortBuffer sbuffer = buffer.asShortBuffer();
1107             sbuffer.get(); // format - not needed.
1108             short numRecords = sbuffer.get();
1109             /* The name table uses unsigned shorts. Many of these
1110              * are known small values that fit in a short.
1111              * The values that are sizes or offsets into the table could be
1112              * greater than 32767, so read and store those as ints
1113              */
1114             int stringPtr = sbuffer.get() & 0xffff;
1115 
1116             nameLocale = sun.awt.SunToolkit.getStartupLocale();
1117             short nameLocaleID = getLCIDFromLocale(nameLocale);
1118 
1119             for (int i=0; i<numRecords; i++) {
1120                 short platformID = sbuffer.get();
1121                 if (platformID != MS_PLATFORM_ID) {
1122                     sbuffer.position(sbuffer.position()+5);
1123                     continue; // skip over this record.
1124                 }
1125                 short encodingID = sbuffer.get();
1126                 short langID     = sbuffer.get();
1127                 short nameID     = sbuffer.get();
1128                 int nameLen    = ((int) sbuffer.get()) & 0xffff;
1129                 int namePtr    = (((int) sbuffer.get()) & 0xffff) + stringPtr;
1130                 String tmpName = null;
1131                 switch (nameID) {
1132 
1133                 case FAMILY_NAME_ID:
1134 
1135                     if (familyName == null || langID == ENGLISH_LOCALE_ID ||
1136                         langID == nameLocaleID)
1137                     {
1138                         buffer.position(namePtr);
1139                         buffer.get(name, 0, nameLen);
1140                         tmpName = makeString(name, nameLen, encodingID);
1141 
1142                         if (familyName == null || langID == ENGLISH_LOCALE_ID){
1143                             familyName = tmpName;
1144                         }
1145                         if (langID == nameLocaleID) {
1146                             localeFamilyName = tmpName;
1147                         }
1148                     }
1149 /*
1150                     for (int ii=0;ii<nameLen;ii++) {
1151                         int val = (int)name[ii]&0xff;
1152                         System.err.print(Integer.toHexString(val)+ " ");
1153                     }
1154                     System.err.println();
1155                     System.err.println("familyName="+familyName +
1156                                        " nameLen="+nameLen+
1157                                        " langID="+langID+ " eid="+encodingID +
1158                                        " str len="+familyName.length());
1159 
1160 */
1161                     break;
1162 
1163                 case FULL_NAME_ID:
1164 
1165                     if (fullName == null || langID == ENGLISH_LOCALE_ID ||
1166                         langID == nameLocaleID)
1167                     {
1168                         buffer.position(namePtr);
1169                         buffer.get(name, 0, nameLen);
1170                         tmpName = makeString(name, nameLen, encodingID);
1171 
1172                         if (fullName == null || langID == ENGLISH_LOCALE_ID) {
1173                             fullName = tmpName;
1174                         }
1175                         if (langID == nameLocaleID) {
1176                             localeFullName = tmpName;
1177                         }
1178                     }
1179                     break;
1180                 }
1181             }
1182             if (localeFamilyName == null) {
1183                 localeFamilyName = familyName;
1184             }
1185             if (localeFullName == null) {
1186                 localeFullName = fullName;
1187             }
1188         }
1189     }
1190 
1191     /* Return the requested name in the requested locale, for the
1192      * MS platform ID. If the requested locale isn't found, return US
1193      * English, if that isn't found, return null and let the caller
1194      * figure out how to handle that.
1195      */
1196     protected String lookupName(short findLocaleID, int findNameID) {
1197         String foundName = null;
1198         byte[] name = new byte[1024];
1199 
1200         ByteBuffer buffer = getTableBuffer(nameTag);
1201         if (buffer != null) {
1202             ShortBuffer sbuffer = buffer.asShortBuffer();
1203             sbuffer.get(); // format - not needed.
1204             short numRecords = sbuffer.get();
1205 
1206             /* The name table uses unsigned shorts. Many of these
1207              * are known small values that fit in a short.
1208              * The values that are sizes or offsets into the table could be
1209              * greater than 32767, so read and store those as ints
1210              */
1211             int stringPtr = ((int) sbuffer.get()) & 0xffff;
1212 
1213             for (int i=0; i<numRecords; i++) {
1214                 short platformID = sbuffer.get();
1215                 if (platformID != MS_PLATFORM_ID) {
1216                     sbuffer.position(sbuffer.position()+5);
1217                     continue; // skip over this record.
1218                 }
1219                 short encodingID = sbuffer.get();
1220                 short langID     = sbuffer.get();
1221                 short nameID     = sbuffer.get();
1222                 int   nameLen    = ((int) sbuffer.get()) & 0xffff;
1223                 int   namePtr    = (((int) sbuffer.get()) & 0xffff) + stringPtr;
1224                 if (nameID == findNameID &&
1225                     ((foundName == null && langID == ENGLISH_LOCALE_ID)
1226                      || langID == findLocaleID)) {
1227                     buffer.position(namePtr);
1228                     buffer.get(name, 0, nameLen);
1229                     foundName = makeString(name, nameLen, encodingID);
1230                     if (langID == findLocaleID) {
1231                         return foundName;
1232                     }
1233                 }
1234             }
1235         }
1236         return foundName;
1237     }
1238 
1239     /**
1240      * @return number of logical fonts. Is "1" for all but TTC files
1241      */
1242     public int getFontCount() {
1243         return directoryCount;
1244     }
1245 
1246     protected synchronized FontScaler getScaler() {
1247         if (scaler == null) {
1248             scaler = FontScaler.getScaler(this, fontIndex,
1249                 supportsCJK, fileSize);
1250         }
1251         return scaler;
1252     }
1253 
1254 
1255     /* Postscript name is rarely requested. Don't waste cycles locating it
1256      * as part of font creation, nor storage to hold it. Get it only on demand.
1257      */
1258     @Override
1259     public String getPostscriptName() {
1260         String name = lookupName(ENGLISH_LOCALE_ID, POSTSCRIPT_NAME_ID);
1261         if (name == null) {
1262             return fullName;
1263         } else {
1264             return name;
1265         }
1266     }
1267 
1268     @Override
1269     public String getFontName(Locale locale) {
1270         if (locale == null) {
1271             return fullName;
1272         } else if (locale.equals(nameLocale) && localeFullName != null) {
1273             return localeFullName;
1274         } else {
1275             short localeID = getLCIDFromLocale(locale);
1276             String name = lookupName(localeID, FULL_NAME_ID);
1277             if (name == null) {
1278                 return fullName;
1279             } else {
1280                 return name;
1281             }
1282         }
1283     }
1284 
1285     // Return a Microsoft LCID from the given Locale.
1286     // Used when getting localized font data.
1287 
1288     private static void addLCIDMapEntry(Map<String, Short> map,
1289                                         String key, short value) {
1290         map.put(key, Short.valueOf(value));
1291     }
1292 
1293     private static synchronized void createLCIDMap() {
1294         if (lcidMap != null) {
1295             return;
1296         }
1297 
1298         Map<String, Short> map = new HashMap<String, Short>(200);
1299 
1300         // the following statements are derived from the langIDMap
1301         // in src/windows/native/java/lang/java_props_md.c using the following
1302         // awk script:
1303         //    $1~/\/\*/   { next}
1304         //    $3~/\?\?/   { next }
1305         //    $3!~/_/     { next }
1306         //    $1~/0x0409/ { next }
1307         //    $1~/0x0c0a/ { next }
1308         //    $1~/0x042c/ { next }
1309         //    $1~/0x0443/ { next }
1310         //    $1~/0x0812/ { next }
1311         //    $1~/0x04/   { print "        addLCIDMapEntry(map, " substr($3, 0, 3) "\", (short) " substr($1, 0, 6) ");" ; next }
1312         //    $3~/,/      { print "        addLCIDMapEntry(map, " $3  " (short) " substr($1, 0, 6) ");" ; next }
1313         //                { print "        addLCIDMapEntry(map, " $3 ", (short) " substr($1, 0, 6) ");" ; next }
1314         // The lines of this script:
1315         // - eliminate comments
1316         // - eliminate questionable locales
1317         // - eliminate language-only locales
1318         // - eliminate the default LCID value
1319         // - eliminate a few other unneeded LCID values
1320         // - print language-only locale entries for x04* LCID values
1321         //   (apparently Microsoft doesn't use language-only LCID values -
1322         //   see http://www.microsoft.com/OpenType/otspec/name.htm
1323         // - print complete entries for all other LCID values
1324         // Run
1325         //     awk -f awk-script langIDMap > statements
1326         addLCIDMapEntry(map, "ar", (short) 0x0401);
1327         addLCIDMapEntry(map, "bg", (short) 0x0402);
1328         addLCIDMapEntry(map, "ca", (short) 0x0403);
1329         addLCIDMapEntry(map, "zh", (short) 0x0404);
1330         addLCIDMapEntry(map, "cs", (short) 0x0405);
1331         addLCIDMapEntry(map, "da", (short) 0x0406);
1332         addLCIDMapEntry(map, "de", (short) 0x0407);
1333         addLCIDMapEntry(map, "el", (short) 0x0408);
1334         addLCIDMapEntry(map, "es", (short) 0x040a);
1335         addLCIDMapEntry(map, "fi", (short) 0x040b);
1336         addLCIDMapEntry(map, "fr", (short) 0x040c);
1337         addLCIDMapEntry(map, "iw", (short) 0x040d);
1338         addLCIDMapEntry(map, "hu", (short) 0x040e);
1339         addLCIDMapEntry(map, "is", (short) 0x040f);
1340         addLCIDMapEntry(map, "it", (short) 0x0410);
1341         addLCIDMapEntry(map, "ja", (short) 0x0411);
1342         addLCIDMapEntry(map, "ko", (short) 0x0412);
1343         addLCIDMapEntry(map, "nl", (short) 0x0413);
1344         addLCIDMapEntry(map, "no", (short) 0x0414);
1345         addLCIDMapEntry(map, "pl", (short) 0x0415);
1346         addLCIDMapEntry(map, "pt", (short) 0x0416);
1347         addLCIDMapEntry(map, "rm", (short) 0x0417);
1348         addLCIDMapEntry(map, "ro", (short) 0x0418);
1349         addLCIDMapEntry(map, "ru", (short) 0x0419);
1350         addLCIDMapEntry(map, "hr", (short) 0x041a);
1351         addLCIDMapEntry(map, "sk", (short) 0x041b);
1352         addLCIDMapEntry(map, "sq", (short) 0x041c);
1353         addLCIDMapEntry(map, "sv", (short) 0x041d);
1354         addLCIDMapEntry(map, "th", (short) 0x041e);
1355         addLCIDMapEntry(map, "tr", (short) 0x041f);
1356         addLCIDMapEntry(map, "ur", (short) 0x0420);
1357         addLCIDMapEntry(map, "in", (short) 0x0421);
1358         addLCIDMapEntry(map, "uk", (short) 0x0422);
1359         addLCIDMapEntry(map, "be", (short) 0x0423);
1360         addLCIDMapEntry(map, "sl", (short) 0x0424);
1361         addLCIDMapEntry(map, "et", (short) 0x0425);
1362         addLCIDMapEntry(map, "lv", (short) 0x0426);
1363         addLCIDMapEntry(map, "lt", (short) 0x0427);
1364         addLCIDMapEntry(map, "fa", (short) 0x0429);
1365         addLCIDMapEntry(map, "vi", (short) 0x042a);
1366         addLCIDMapEntry(map, "hy", (short) 0x042b);
1367         addLCIDMapEntry(map, "eu", (short) 0x042d);
1368         addLCIDMapEntry(map, "mk", (short) 0x042f);
1369         addLCIDMapEntry(map, "tn", (short) 0x0432);
1370         addLCIDMapEntry(map, "xh", (short) 0x0434);
1371         addLCIDMapEntry(map, "zu", (short) 0x0435);
1372         addLCIDMapEntry(map, "af", (short) 0x0436);
1373         addLCIDMapEntry(map, "ka", (short) 0x0437);
1374         addLCIDMapEntry(map, "fo", (short) 0x0438);
1375         addLCIDMapEntry(map, "hi", (short) 0x0439);
1376         addLCIDMapEntry(map, "mt", (short) 0x043a);
1377         addLCIDMapEntry(map, "se", (short) 0x043b);
1378         addLCIDMapEntry(map, "gd", (short) 0x043c);
1379         addLCIDMapEntry(map, "ms", (short) 0x043e);
1380         addLCIDMapEntry(map, "kk", (short) 0x043f);
1381         addLCIDMapEntry(map, "ky", (short) 0x0440);
1382         addLCIDMapEntry(map, "sw", (short) 0x0441);
1383         addLCIDMapEntry(map, "tt", (short) 0x0444);
1384         addLCIDMapEntry(map, "bn", (short) 0x0445);
1385         addLCIDMapEntry(map, "pa", (short) 0x0446);
1386         addLCIDMapEntry(map, "gu", (short) 0x0447);
1387         addLCIDMapEntry(map, "ta", (short) 0x0449);
1388         addLCIDMapEntry(map, "te", (short) 0x044a);
1389         addLCIDMapEntry(map, "kn", (short) 0x044b);
1390         addLCIDMapEntry(map, "ml", (short) 0x044c);
1391         addLCIDMapEntry(map, "mr", (short) 0x044e);
1392         addLCIDMapEntry(map, "sa", (short) 0x044f);
1393         addLCIDMapEntry(map, "mn", (short) 0x0450);
1394         addLCIDMapEntry(map, "cy", (short) 0x0452);
1395         addLCIDMapEntry(map, "gl", (short) 0x0456);
1396         addLCIDMapEntry(map, "dv", (short) 0x0465);
1397         addLCIDMapEntry(map, "qu", (short) 0x046b);
1398         addLCIDMapEntry(map, "mi", (short) 0x0481);
1399         addLCIDMapEntry(map, "ar_IQ", (short) 0x0801);
1400         addLCIDMapEntry(map, "zh_CN", (short) 0x0804);
1401         addLCIDMapEntry(map, "de_CH", (short) 0x0807);
1402         addLCIDMapEntry(map, "en_GB", (short) 0x0809);
1403         addLCIDMapEntry(map, "es_MX", (short) 0x080a);
1404         addLCIDMapEntry(map, "fr_BE", (short) 0x080c);
1405         addLCIDMapEntry(map, "it_CH", (short) 0x0810);
1406         addLCIDMapEntry(map, "nl_BE", (short) 0x0813);
1407         addLCIDMapEntry(map, "no_NO_NY", (short) 0x0814);
1408         addLCIDMapEntry(map, "pt_PT", (short) 0x0816);
1409         addLCIDMapEntry(map, "ro_MD", (short) 0x0818);
1410         addLCIDMapEntry(map, "ru_MD", (short) 0x0819);
1411         addLCIDMapEntry(map, "sr_CS", (short) 0x081a);
1412         addLCIDMapEntry(map, "sv_FI", (short) 0x081d);
1413         addLCIDMapEntry(map, "az_AZ", (short) 0x082c);
1414         addLCIDMapEntry(map, "se_SE", (short) 0x083b);
1415         addLCIDMapEntry(map, "ga_IE", (short) 0x083c);
1416         addLCIDMapEntry(map, "ms_BN", (short) 0x083e);
1417         addLCIDMapEntry(map, "uz_UZ", (short) 0x0843);
1418         addLCIDMapEntry(map, "qu_EC", (short) 0x086b);
1419         addLCIDMapEntry(map, "ar_EG", (short) 0x0c01);
1420         addLCIDMapEntry(map, "zh_HK", (short) 0x0c04);
1421         addLCIDMapEntry(map, "de_AT", (short) 0x0c07);
1422         addLCIDMapEntry(map, "en_AU", (short) 0x0c09);
1423         addLCIDMapEntry(map, "fr_CA", (short) 0x0c0c);
1424         addLCIDMapEntry(map, "sr_CS", (short) 0x0c1a);
1425         addLCIDMapEntry(map, "se_FI", (short) 0x0c3b);
1426         addLCIDMapEntry(map, "qu_PE", (short) 0x0c6b);
1427         addLCIDMapEntry(map, "ar_LY", (short) 0x1001);
1428         addLCIDMapEntry(map, "zh_SG", (short) 0x1004);
1429         addLCIDMapEntry(map, "de_LU", (short) 0x1007);
1430         addLCIDMapEntry(map, "en_CA", (short) 0x1009);
1431         addLCIDMapEntry(map, "es_GT", (short) 0x100a);
1432         addLCIDMapEntry(map, "fr_CH", (short) 0x100c);
1433         addLCIDMapEntry(map, "hr_BA", (short) 0x101a);
1434         addLCIDMapEntry(map, "ar_DZ", (short) 0x1401);
1435         addLCIDMapEntry(map, "zh_MO", (short) 0x1404);
1436         addLCIDMapEntry(map, "de_LI", (short) 0x1407);
1437         addLCIDMapEntry(map, "en_NZ", (short) 0x1409);
1438         addLCIDMapEntry(map, "es_CR", (short) 0x140a);
1439         addLCIDMapEntry(map, "fr_LU", (short) 0x140c);
1440         addLCIDMapEntry(map, "bs_BA", (short) 0x141a);
1441         addLCIDMapEntry(map, "ar_MA", (short) 0x1801);
1442         addLCIDMapEntry(map, "en_IE", (short) 0x1809);
1443         addLCIDMapEntry(map, "es_PA", (short) 0x180a);
1444         addLCIDMapEntry(map, "fr_MC", (short) 0x180c);
1445         addLCIDMapEntry(map, "sr_BA", (short) 0x181a);
1446         addLCIDMapEntry(map, "ar_TN", (short) 0x1c01);
1447         addLCIDMapEntry(map, "en_ZA", (short) 0x1c09);
1448         addLCIDMapEntry(map, "es_DO", (short) 0x1c0a);
1449         addLCIDMapEntry(map, "sr_BA", (short) 0x1c1a);
1450         addLCIDMapEntry(map, "ar_OM", (short) 0x2001);
1451         addLCIDMapEntry(map, "en_JM", (short) 0x2009);
1452         addLCIDMapEntry(map, "es_VE", (short) 0x200a);
1453         addLCIDMapEntry(map, "ar_YE", (short) 0x2401);
1454         addLCIDMapEntry(map, "es_CO", (short) 0x240a);
1455         addLCIDMapEntry(map, "ar_SY", (short) 0x2801);
1456         addLCIDMapEntry(map, "en_BZ", (short) 0x2809);
1457         addLCIDMapEntry(map, "es_PE", (short) 0x280a);
1458         addLCIDMapEntry(map, "ar_JO", (short) 0x2c01);
1459         addLCIDMapEntry(map, "en_TT", (short) 0x2c09);
1460         addLCIDMapEntry(map, "es_AR", (short) 0x2c0a);
1461         addLCIDMapEntry(map, "ar_LB", (short) 0x3001);
1462         addLCIDMapEntry(map, "en_ZW", (short) 0x3009);
1463         addLCIDMapEntry(map, "es_EC", (short) 0x300a);
1464         addLCIDMapEntry(map, "ar_KW", (short) 0x3401);
1465         addLCIDMapEntry(map, "en_PH", (short) 0x3409);
1466         addLCIDMapEntry(map, "es_CL", (short) 0x340a);
1467         addLCIDMapEntry(map, "ar_AE", (short) 0x3801);
1468         addLCIDMapEntry(map, "es_UY", (short) 0x380a);
1469         addLCIDMapEntry(map, "ar_BH", (short) 0x3c01);
1470         addLCIDMapEntry(map, "es_PY", (short) 0x3c0a);
1471         addLCIDMapEntry(map, "ar_QA", (short) 0x4001);
1472         addLCIDMapEntry(map, "es_BO", (short) 0x400a);
1473         addLCIDMapEntry(map, "es_SV", (short) 0x440a);
1474         addLCIDMapEntry(map, "es_HN", (short) 0x480a);
1475         addLCIDMapEntry(map, "es_NI", (short) 0x4c0a);
1476         addLCIDMapEntry(map, "es_PR", (short) 0x500a);
1477 
1478         lcidMap = map;
1479     }
1480 
1481     private static short getLCIDFromLocale(Locale locale) {
1482         // optimize for common case
1483         if (locale.equals(Locale.US)) {
1484             return US_LCID;
1485         }
1486 
1487         if (lcidMap == null) {
1488             createLCIDMap();
1489         }
1490 
1491         String key = locale.toString();
1492         while (!"".equals(key)) {
1493             Short lcidObject = (Short) lcidMap.get(key);
1494             if (lcidObject != null) {
1495                 return lcidObject.shortValue();
1496             }
1497             int pos = key.lastIndexOf('_');
1498             if (pos < 1) {
1499                 return US_LCID;
1500             }
1501             key = key.substring(0, pos);
1502         }
1503 
1504         return US_LCID;
1505     }
1506 
1507     @Override
1508     public String getFamilyName(Locale locale) {
1509         if (locale == null) {
1510             return familyName;
1511         } else if (locale.equals(nameLocale) && localeFamilyName != null) {
1512             return localeFamilyName;
1513         } else {
1514             short localeID = getLCIDFromLocale(locale);
1515             String name = lookupName(localeID, FAMILY_NAME_ID);
1516             if (name == null) {
1517                 return familyName;
1518             } else {
1519                 return name;
1520             }
1521         }
1522     }
1523 
1524     public CharToGlyphMapper getMapper() {
1525         if (mapper == null) {
1526             mapper = new TrueTypeGlyphMapper(this);
1527         }
1528         return mapper;
1529     }
1530 
1531     /* This duplicates initNames() but that has to run fast as its used
1532      * during typical start-up and the information here is likely never
1533      * needed.
1534      */
1535     protected void initAllNames(int requestedID, HashSet names) {
1536 
1537         byte[] name = new byte[256];
1538         ByteBuffer buffer = getTableBuffer(nameTag);
1539 
1540         if (buffer != null) {
1541             ShortBuffer sbuffer = buffer.asShortBuffer();
1542             sbuffer.get(); // format - not needed.
1543             short numRecords = sbuffer.get();
1544 
1545             /* The name table uses unsigned shorts. Many of these
1546              * are known small values that fit in a short.
1547              * The values that are sizes or offsets into the table could be
1548              * greater than 32767, so read and store those as ints
1549              */
1550             int stringPtr = ((int) sbuffer.get()) & 0xffff;
1551             for (int i=0; i<numRecords; i++) {
1552                 short platformID = sbuffer.get();
1553                 if (platformID != MS_PLATFORM_ID) {
1554                     sbuffer.position(sbuffer.position()+5);
1555                     continue; // skip over this record.
1556                 }
1557                 short encodingID = sbuffer.get();
1558                 short langID     = sbuffer.get();
1559                 short nameID     = sbuffer.get();
1560                 int   nameLen    = ((int) sbuffer.get()) & 0xffff;
1561                 int   namePtr    = (((int) sbuffer.get()) & 0xffff) + stringPtr;
1562 
1563                 if (nameID == requestedID) {
1564                     buffer.position(namePtr);
1565                     buffer.get(name, 0, nameLen);
1566                     names.add(makeString(name, nameLen, encodingID));
1567                 }
1568             }
1569         }
1570     }
1571 
1572     String[] getAllFamilyNames() {
1573         HashSet aSet = new HashSet();
1574         try {
1575             initAllNames(FAMILY_NAME_ID, aSet);
1576         } catch (Exception e) {
1577             /* In case of malformed font */
1578         }
1579         return (String[])aSet.toArray(new String[0]);
1580     }
1581 
1582     String[] getAllFullNames() {
1583         HashSet aSet = new HashSet();
1584         try {
1585             initAllNames(FULL_NAME_ID, aSet);
1586         } catch (Exception e) {
1587             /* In case of malformed font */
1588         }
1589         return (String[])aSet.toArray(new String[0]);
1590     }
1591 
1592     /*  Used by the OpenType engine for mark positioning.
1593      */
1594     @Override
1595     Point2D.Float getGlyphPoint(long pScalerContext,
1596                                 int glyphCode, int ptNumber) {
1597         try {
1598             return getScaler().getGlyphPoint(pScalerContext,
1599                                              glyphCode, ptNumber);
1600         } catch(FontScalerException fe) {
1601             return null;
1602         }
1603     }
1604 
1605     private char[] gaspTable;
1606 
1607     private char[] getGaspTable() {
1608 
1609         if (gaspTable != null) {
1610             return gaspTable;
1611         }
1612 
1613         ByteBuffer buffer = getTableBuffer(gaspTag);
1614         if (buffer == null) {
1615             return gaspTable = new char[0];
1616         }
1617 
1618         CharBuffer cbuffer = buffer.asCharBuffer();
1619         char format = cbuffer.get();
1620         /* format "1" has appeared for some Windows Vista fonts.
1621          * Its presently undocumented but the existing values
1622          * seem to be still valid so we can use it.
1623          */
1624         if (format > 1) { // unrecognised format
1625             return gaspTable = new char[0];
1626         }
1627 
1628         char numRanges = cbuffer.get();
1629         if (4+numRanges*4 > getTableSize(gaspTag)) { // sanity check
1630             return gaspTable = new char[0];
1631         }
1632         gaspTable = new char[2*numRanges];
1633         cbuffer.get(gaspTable);
1634         return gaspTable;
1635     }
1636 
1637     /* This is to obtain info from the TT 'gasp' (grid-fitting and
1638      * scan-conversion procedure) table which specifies three combinations:
1639      * Hint, Smooth (greyscale), Hint and Smooth.
1640      * In this simplified scheme we don't distinguish the latter two. We
1641      * hint even at small sizes, so as to preserve metrics consistency.
1642      * If the information isn't available default values are substituted.
1643      * The more precise defaults we'd do if we distinguished the cases are:
1644      * Bold (no other style) fonts :
1645      * 0-8 : Smooth ( do grey)
1646      * 9+  : Hint + smooth (gridfit + grey)
1647      * Plain, Italic and Bold-Italic fonts :
1648      * 0-8 : Smooth ( do grey)
1649      * 9-17 : Hint (gridfit)
1650      * 18+  : Hint + smooth (gridfit + grey)
1651      * The defaults should rarely come into play as most TT fonts provide
1652      * better defaults.
1653      * REMIND: consider unpacking the table into an array of booleans
1654      * for faster use.
1655      */
1656     @Override
1657     public boolean useAAForPtSize(int ptsize) {
1658 
1659         char[] gasp = getGaspTable();
1660         if (gasp.length > 0) {
1661             for (int i=0;i<gasp.length;i+=2) {
1662                 if (ptsize <= gasp[i]) {
1663                     return ((gasp[i+1] & 0x2) != 0); // bit 2 means DO_GRAY;
1664                 }
1665             }
1666             return true;
1667         }
1668 
1669         if (style == Font.BOLD) {
1670             return true;
1671         } else {
1672             return ptsize <= 8 || ptsize >= 18;
1673         }
1674     }
1675 
1676     @Override
1677     public boolean hasSupplementaryChars() {
1678         return ((TrueTypeGlyphMapper)getMapper()).hasSupplementaryChars();
1679     }
1680 
1681     @Override
1682     public String toString() {
1683         return "** TrueType Font: Family="+familyName+ " Name="+fullName+
1684             " style="+style+" fileName="+getPublicFileName();
1685     }
1686 }